/**
 *Copyright (c) 2024 watereyes
 * workflow is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package com.koron.workflow.api.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.koron.ebs.mybatis.ADOConnection;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.SqlTask;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.swan.bean.MessageBean;

import com.koron.beetl.BeetlTool;
import com.koron.util.Constant;
import com.koron.util.encode.Base64;
import com.koron.util.encode.RSAEncrypt;
import com.koron.workflow.api.rule.FilterRuleStrategy;
import com.koron.workflow.api.rule.FilterRuleStrategy1;
import com.koron.workflow.api.rule.FilterRuleStrategy2;
import com.koron.workflow.api.rule.FilterRuleStrategy3;
import com.koron.workflow.api.rule.FilterRuleStrategy4;
import com.koron.workflow.api.rule.FilterRuleStrategy5;
import com.koron.workflow.api.rule.FilterRuleStrategy6;
import com.koron.workflow.api.rule.FilterRuleStrategy7;
import com.koron.workflow.api.service.WorkflowService;
import com.koron.workflow.applicationManag.bean.AppBean;
import com.koron.workflow.applicationManag.mapper.AppManagMapper;
import com.koron.workflow.baseConfig.bean.DepartmentBean;
import com.koron.workflow.baseConfig.mapper.StaffMapper;
import com.koron.workflow.baseConfig.service.PostService;
import com.koron.workflow.baseConfig.service.RoleLineService;
import com.koron.workflow.common.bean.StaffBean;
import com.koron.workflow.common.util.BusinessCode;
import com.koron.workflow.common.util.SpringContextHolder;
import com.koron.workflow.common.util.WorkflowUtil;
import com.koron.workflow.workflowDesign.bean.ProcessBean;
import com.koron.workflow.workflowDesign.mapper.ProcessMapper;

@Component
public class ApiUtils {
	
	private static long timestampExpire = 30;
	
	private static PostService postService = SpringContextHolder.getBean(PostService.class);
	
	private static RoleLineService roleLineService = SpringContextHolder.getBean(RoleLineService.class);

	@Value("${timestamp-expire}")
	public static void setTimestampExpire(long timestampExpire) {
		ApiUtils.timestampExpire = timestampExpire;
	}
	
	public static String transOperation(int operation) {
		String value = "";
		switch (operation) {
			case WorkflowService.OPERATION_REJECT:{
				value = "驳回";
				break;
			}
			case WorkflowService.OPERATION_AGREE:{
				value = "同意";
				break;
			}
			case WorkflowService.OPERATION_ALRAM:{
				value = "催办";
				break;
			}
			case WorkflowService.OPERATION_CANCLE_COMMUNICATE:{
				value = "取消沟通";
				break;
			}
			case WorkflowService.OPERATION_COMMUNICATE:{
				value = "沟通";
				break;
			}
			case WorkflowService.OPERATION_REPLY:{
				value = "回复";
				break;
			}
			case WorkflowService.OPERATION_SUSPEND:{
				value = "废弃";
				break;
			}
			case WorkflowService.OPERATION_TRANSFER:{
				value = "转办";
				break;
			}
			case WorkflowService.OPERATION_RECALL:{
				value = "撤回";
				break;
			}
			default:
				break;
		}
		return value;
	}

	/**
	 * 使用私钥解密
	 *
	 * @param secret
	 * 			@return appid @throws
	 */
	public static String decrypt(String secret) {
		try {
			Objects.requireNonNull(secret, "secret 参数不能为空");
			return RSAEncrypt.decrypt(RSAEncrypt.loadPrivateKeyByStr(Constant.RSAprivatekey), Base64.decode(secret));
		} catch (Exception e) {
			throw new RuntimeException("secret 解析失败");
		}
	}
	
	/**
	 * 使用公钥加密
	 * @param text
	 * @return
	 */
	public static String encrypt(String text) {
		try {
			Objects.requireNonNull(text, "要加密的内容不能为空");
			return RSAEncrypt.encrypt(RSAEncrypt.loadPublicKeyByStr(Constant.RSApublickey), text.getBytes("UTF-8"));
		} catch (Exception e) {
			throw new RuntimeException("加密失败");
		}
	}

	public static MessageBean<?> secretVerify(String secret) {
		String text = decrypt(secret);
		//时间戳与appId 横杠分隔
		String[] array = text.split("-");
		final String appCode = array[0];
		String timestamp = array[1];
		if(System.currentTimeMillis() - Long.parseLong(timestamp) > (timestampExpire * 1000)) {
			return MessageBean.create(BusinessCode.FAILURE.getCode(), "调用时间失效，请同步时钟", Void.class);
		}
		return ADOConnection.runTask(new SqlTask() {
			@Override
			public MessageBean<?> run(SessionFactory factory) {
				AppBean bean = factory.getMapper(AppManagMapper.class).queryByCode(appCode);
				if (bean == null) {
					return MessageBean.create(BusinessCode.FAILURE.getCode(), "秘钥匹配失败", Void.class);
				}
				if (bean.getStatus() == Constant.STATUS_FORBIDDEN) {
					return MessageBean.create(BusinessCode.FAILURE.getCode(), "该应用已禁用", Void.class);
				}
				MessageBean<String> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "匹配成功", String.class);
				message.setData(appCode);
				return message;
			}
		}, MessageBean.class);
	}

	/**
	 * 流程模板权限校验
	 * @param appCode
	 * @param processCode
	 * @param setCode
	 * @return
	 * @throws Exception
	 */
	public static MessageBean<?> processPermission(String appCode, String processCode, String setCode) {
		return ADOConnection.runTask(new SqlTask() {
			@Override
			public Object run(SessionFactory factory) {
				ProcessBean proess = factory.getMapper(ProcessMapper.class).queryByCodeAndSetCode(processCode, setCode);
				if (proess == null) {
					return MessageBean.create(BusinessCode.FAILURE.getCode(), "工作流模板组编号与工作流模板编号对应不上 ", Void.class);
				}
				AppManagMapper appMapper = factory.getMapper(AppManagMapper.class);
				AppBean app = appMapper.queryByCode(appCode);
				if (app == null || !Objects.equals(proess.getAppid(), app.getId())) {
					return MessageBean.create(BusinessCode.FAILURE.getCode(), "无权限操作指定的流程模板", Void.class);
				}
				MessageBean<ProcessBean> message = MessageBean.create(BusinessCode.SUCCESS.getCode(), "success", ProcessBean.class);
				message.setData(proess);
				return message;
			}
		}, MessageBean.class);
	}

	@SuppressWarnings("unchecked")
	public static List<String> convertUsers(List<String> users, String operator, String filterRule, String autoPickRule, Map<String, Object> variables){
		List<StaffBean> staffs = convertStaff(users, variables, operator);
		//根据过滤规则过滤
		if(StringUtils.isNotBlank(filterRule)) {
			StaffBean staff = WorkflowUtil.getStaffByAccount(operator);
			FilterRuleStrategy rule = getFilterRuleStrategy(Integer.parseInt(filterRule));
			if(rule != null) {
				List<StaffBean> ss = ADOConnection.runTask(new SqlTask() {
					@Override
					public Object run(SessionFactory factory) {
						int i = Integer.parseInt(StringUtils.defaultIfEmpty(autoPickRule, "0"));
						return rule.filter(factory, staffs, staff, i);
					}
				}, List.class);
				Set<String> accounts = ss.stream().map(s -> s.getAccount()).collect(Collectors.toSet());
				return new ArrayList<>(accounts);
			}
		}
		Set<String> accounts = staffs.stream().filter(t -> t != null).map(s -> s.getAccount()).collect(Collectors.toSet());
		return new ArrayList<>(accounts);
	}

	/**
	 * 根据code把用户表达式转换成用户列表
	 * @param code 1代表用户,2代表部门4代表群组8代表表达式
	 * @param map 变量
	 * @param startUser
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static final List<StaffBean> convertUsers(String code, final Map<String, Object> map, String startUser) {
		List<StaffBean> staffs = new ArrayList<StaffBean>();
		if (code.startsWith("1-")){  // 个人
			StaffBean staff = ADOConnection.runTask(new SqlTask() {
				@Override
				public Object run(SessionFactory factory) {
					return factory.getMapper(StaffMapper.class).queryByAccount(code.substring(2));
				}
			}, StaffBean.class);
			if(staff != null) {
				staffs.add(staff);
			}
		} else if (code.startsWith("2-")) {  // 部门
			staffs.addAll(ADOConnection.runTask(factory -> {
				return factory.getMapper(StaffMapper.class).queryByDepartmentCode(code.substring(2));
			}, List.class));
		} else if (code.startsWith("8-")) { // 角色线
			List<StaffBean> ss = ADOConnection.runTask(new SqlTask() {
				@Override
				public Object run(SessionFactory factory) {
					return roleLineService.queryForRoleId(factory, code.substring(2), startUser);
				}
			}, List.class);
			staffs.addAll(ss);
		} else if (code.startsWith("16-")){  // 表达式
			staffs.addAll(processTemplate(map, code.substring(3)));
		}else if (code.startsWith("4-")){  // 岗位
			List<StaffBean> ss = ADOConnection.runTask(new SqlTask() {
				@Override
				public Object run(SessionFactory factory) {
					return postService.queryPostStaff(factory, code.substring(2));
				}
			}, List.class);
			staffs.addAll(ss);
		}
		return staffs;
	}
	
	private static FilterRuleStrategy getFilterRuleStrategy(int filterRule) {
		FilterRuleStrategy filterRuleStrategy = null;
		switch (filterRule) {
			case 1:{
				filterRuleStrategy = new FilterRuleStrategy1();
				break;
			}
			case 2:{
				filterRuleStrategy = new FilterRuleStrategy2();
				break;
			}
			case 3:{
				filterRuleStrategy = new FilterRuleStrategy3();
				break;
			}
			case 4:{
				filterRuleStrategy = new FilterRuleStrategy4();
				break;
			}
			case 5:{
				filterRuleStrategy = new FilterRuleStrategy5();
				break;
			}
			case 6:{
				filterRuleStrategy = new FilterRuleStrategy6();
				break;
			}
			case 7:{
				filterRuleStrategy = new FilterRuleStrategy7();
				break;
			}
			default:
				break;
		}
		return filterRuleStrategy;
	}

	/**
	 *
	 * @param variables
	 * @param template
	 * @return
	 */
	private static final List<StaffBean> processTemplate(Map<String, Object> variables, String template) {
		BeetlTool bt = BeetlTool.getIntance();
		List<StaffBean> staffs = new ArrayList<StaffBean>();
		for (Map.Entry<String, Object> key : variables.entrySet()) {
			bt.put(key.getKey(), key.getValue());
		}
		String result = bt.render(template);
		if (result.indexOf(',') == -1)// 如果解析出来是单个（人或部门，直接返回）
		{
			staffs.add(WorkflowUtil.getStaffByAccount(result));
			return staffs;
		} else {// 表达式返回多个值
			List<String> expressions = Arrays.asList(result.split(","));
			staffs.addAll(WorkflowUtil.getStaffByAccount(expressions));
			return staffs;
		}
	}
	
	private static List<StaffBean> convertStaff(List<String> users, final Map<String, Object> variables, String startUser) {
		List<StaffBean> tmpStaff = new ArrayList<StaffBean>();
		if (users != null) {
			for (String user : users) {
				tmpStaff.addAll(convertUsers(user, variables, startUser));
			}
		}
		return tmpStaff;
	}

}
